##############
# (*) Kaleidoscope
# By: Allan Spale
# July 2008

from __future__ import with_statement
from contextlib import contextmanager

#from Kaleidoscope.KSTransaction import KSTransaction, KSTransactionError
from Kaleidoscope.KSEventManager import KSEventManager, KSEventManagerError

import mx.DateTime



class KSUtility(object):
    # Epoch
    # All dates marked in seconds will be good for over 120 years
    # It should be noted that these are Kaleidoscope system dates and that
    # different dates would be used for any other dates
    DATE_EPOCH = mx.DateTime.Date(2008, 07, 14)
    DATE_EPOCH_SECONDS = int(DATE_EPOCH.gmticks())
    MAX_IDS_PER_SECOND = 30
    MAX_IDS_ALLOWED_PER_TABLE = int(((2**32)-1)/MAX_IDS_PER_SECOND)

    """
    This is a utility class that does many repetitive tasks involving contexts
    of transactions, type checking, and event sending.

    """
    def __init__(self, ksem_event_manager, fn_get_transaction):
        """
        Pass the constantly used items related to KSEventManager and
        KSTransactionFactory.

        """
        if not (isinstance(ksem_event_manager, KSEventManager)):
            raise Exception(u_message)

        if not (callable(fn_get_transaction)):
            raise Exception(u_message)

        self.__ksem_event_manager = ksem_event_manager
        self.__fn_get_transaction = fn_get_transaction


    @contextmanager
    def transaction(self, kstx_parent_transaction, fn_current):
        """
        Transaction context

        """
        try:
            kstx_transaction = self.__fn_get_transaction(
                kstx_parent_transaction, fn_current)
            yield kstx_transaction
        except Exception:
            kstx_transaction.cancel()
            raise Exception('')
        else:
            kstx_transaction.commit(0, fn_current)


    def send_before_event(self, fn_current, list_param_values):
        """
        Event sending

        """
        try:
            self.__ksem_event_manager.send_before_event_message(
                fn_current, list_param_values)
            return True
        except Exception:
            KSEventManagerError.print_and_raise_error('Sending of '
                + u'"before event" failed because of an unknown error.',
                KSEventManagerError.B_CLASS_EXCEPTION)


    @staticmethod
    def type_check(list_param_types, list_param_values):
        """
        Type-check

        """
        counter = 1
        for a_tuple in zip(list_param_values, list_param_types):
            if not(isinstance(a_tuple[0], a_tuple[1])):
                raise TypeError, 'A valid %s is required for parameter %d.' \
                    % (str(a_tuple[1]), counter)
            else:
                counter = counter + 1
        yield True


    def type_check_and_send_event(self, fn_current, list_param_types,
                                      list_param_values):
        """
        Check the values in list_param_values and make sure that they match
        the types listed in list_param_types.

        """
        try:
            self.__ksem_event_manager.send_before_event_message(
                fn_current, list_param_values)

            counter = 1
            for a_tuple in zip(list_param_values, list_param_types):
                if not(isinstance(a_tuple[0], a_tuple[1])):
                    raise TypeError, 'A valid %s is required for parameter ' \
                        + u'%d.' % (str(a_tuple[1]), counter)
                else:
                    counter = counter + 1
            return True

        except Exception:
            KSEventManagerError.print_and_raise_error('Sending of "before '
                + u'event" failed because of an unknown error.',
                KSEventManagerError.B_CLASS_EXCEPTION)


    @contextmanager
    def complete_transaction(self, kstx_parent_transaction,
            fn_current, list_param_types, list_param_values):
        """
        Complete transaction that supports transaction checking, event sending,
        and type

        """
        try:
            counter = 1
            for a_tuple in zip(list_param_values, list_param_types):
                if not(isinstance(a_tuple[0], a_tuple[1])):
                    if (a_tuple[0] != None):
                        raise TypeError, 'A valid %s is required for ' \
                            + u'parameter %d.' % (str(a_tuple[1]), counter)
                else:
                    counter = counter + 1

            kstx_transaction = self.__fn_get_transaction(
                kstx_parent_transaction, fn_current)
            self.__ksem_event_manager.send_before_event_message(
                fn_current, list_param_values)

            yield kstx_transaction

        except KSEventManagerError:
            kstx_transaction.cancel()
            KSEventManagerError.print_and_raise_error(u'A problem occurred '
                + u'trying to send an event.',
                KSEventManagerError.B_UNKNOWN_EXCEPTION)

        except Exception:
            kstx_transaction.cancel()
            raise Exception('')

        else:
            kstx_transaction.commit(0, fn_current)